﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Collections;

namespace TAS.XtOM
{
	public abstract class XmlMappedObject : IXmlMappedObject
	{
		public static void MapXmlData(IXmlMappedObject mappedObject, string xml)
		{
			XmlDocument doc = new XmlDocument();
			doc.LoadXml(xml);

			MapXmlData(mappedObject, doc, null);
		}

		public static void MapXmlData(IXmlMappedObject mappedObject, XmlDocument xmlDocument)
		{
			MapXmlData(mappedObject, xmlDocument, null);
		}

		public static void MapXmlData(IXmlMappedObject mappedObject, XmlDocument xmlDocument, XmlNamespaceManager manager)
		{
			foreach (var property in mappedObject.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(MapperInstructionsAttribute), true).Count() > 0))
			{
				MapperInstructionsAttribute attribute = property.GetCustomAttributes(typeof(MapperInstructionsAttribute), true).FirstOrDefault() as MapperInstructionsAttribute;

				XmlNode node = manager == null ? xmlDocument.SelectSingleNode(attribute.XPath) : xmlDocument.SelectSingleNode(attribute.XPath, manager);

				if (node != null)
				{
					// Check if type convertion is set for current property.
					object value;
					if (attribute.TypeConvertionEnabled)
						value = Convert.ChangeType(attribute.ValueSelector.SelectValue(node), attribute.ConvertTo);
					else
						value = attribute.ValueSelector.SelectValue(node);

					if (attribute.NodeType == NodeType.SingleNode)
						property.SetValue(mappedObject, value, null);
					else
					{
						IList list = property.GetValue(mappedObject, null) as IList;
						foreach (XmlNode child in node.ChildNodes)
						{
							IXmlMappedObject obj = Activator.CreateInstance(attribute.NodeListType) as IXmlMappedObject;
							if (obj != null)
							{
								if (manager == null)
									obj.MapXmlData(child.OuterXml);
								else
								{
									XmlDocument xmlDoc = new XmlDocument();
									xmlDoc.LoadXml(child.OuterXml);
									obj.MapXmlData(xmlDoc, manager);
								}
								list.Add(obj);
							}
						}
					}
				}
			}
		}

		public void MapXmlData(string xml)
		{
			MapXmlData(this, xml);
		}

		public void MapXmlData(XmlDocument xmlDocument)
		{
			MapXmlData(this, xmlDocument);
		}

		public void MapXmlData(XmlDocument xmlDocument, XmlNamespaceManager manager)
		{
			MapXmlData(this, xmlDocument, manager);
		}
	}
}
